#include <SocketTCP.h>
#include <Address.h>
#include <SocketException.h>
#include <sys/types.h>
//#include <sys/socket.h>
#include <string.h>
//#include <sys/uio.h>
//#include <unistd.h>
#include <iostream>
#include <errno.h>
//#include <pthread.h>
#include <fcntl.h>
//#include <netinet/in.h>
//#include <arpa/inet.h>
extern int errno;

// Constructores
SocketTCP::SocketTCP(SocketTCP &stcp)
{
   type=stcp.type;
   addr=stcp.addr;
   sd=stcp.sd;
   tc=stcp.tc;
   tc.reset();
   memmove(&saddrl, &stcp.saddrl, sizeof(struct sockaddr_in));
   memmove(&saddrr, &stcp.saddrr, sizeof(struct sockaddr_in));
//   cout << __FILE__ << " (" << __LINE__ << ") " << "[" << pthread_self() << "] [CONIA]\tSD=" << sd << "\terrno=" << errno << "\tthis=" << this << endl;
}

SocketTCP::SocketTCP(Address addr, enum SType type, int qlen)
{
   this->type=type;
   this->addr=addr;

   sd=socket(PF_INET, SOCK_STREAM, 0);
   if (sd<0)
      throw SocketException(string("Unable to create socket"));

   // blocking per collons
/*   int fl=fcntl(sd, F_GETFL);
   fl&=~O_NONBLOCK;
   fcntl(sd, F_SETFL, fl);*/
   
   switch (type)
   {
    case SOCK_CONNECT: 
      memset(&saddrl, 0, sizeof(struct sockaddr_in));
      memset(&saddrr, 0, sizeof(struct sockaddr_in));
#ifdef BSD
       saddrr.sin_len=sizeof(struct sockaddr_in);
#endif
      saddrr.sin_family=AF_INET;
      saddrr.sin_port=htons(addr.getPort());
      saddrr.sin_addr.s_addr=addr.getIP().toDWord();
      break;
      
    case SOCK_SERVE:
      memset(&saddrr, 0, sizeof(struct sockaddr_in));
      memset(&saddrl, 0, sizeof(struct sockaddr_in));
#ifdef BSD
       saddrl.sin_len=sizeof(struct sockaddr_in);
#endif
      saddrl.sin_family=AF_INET;
      saddrl.sin_port=htons(addr.getPort());
      saddrl.sin_addr.s_addr=addr.getIP().toDWord();
      
      if (bind(sd, (struct sockaddr*)&saddrl, sizeof(struct sockaddr_in))<0)
	 throw SocketException(string("Unable to bind socket to ").
			       append(addr.toString()));

      if (listen(sd, qlen)<0)
	 throw SocketException(string("Unable to listen on socket"));

      break;
      
   }
   
//   cout << __FILE__ << " (" << __LINE__ << ") " << "[" << pthread_self() << "] [CONTR]\tSD=" << sd << "\terrno=" << errno << "\tthis=" << this << endl;
   
}

SocketTCP::~SocketTCP()
{
   close();
}

// Metodes
int SocketTCP::read(void *buf, size_t n)
{
  
//   cout << __FILE__ << " (" << __LINE__ << ") " << "-> [" << pthread_self() << "] SocketTCP::Read; this=" << this << ", sd=" << sd << endl;
   int b;

   do
   {
      b=recv(sd, (char*)buf, n, 0);
//      cout << __FILE__ << " (" << __LINE__ << ") " << "errno = " << errno << ", sd= " << sd << endl;
   } while (errno==EINTR);
      
   if (b<0 || (errno!=0 && errno!=EAGAIN))
      throw SocketException(string("Error reading from socket"));

//   tc.addR(b);
   
//   cout << __FILE__ << " (" << __LINE__ << ") " << "<- [" << pthread_self() << "] SocketTCP::Read; this=" << this << ", sd=" << sd << endl;   
//   
//   
//   cout << __FILE__ << " (" << __LINE__ << ") " << "[" << pthread_self() << "] [READ]\tSD=" << sd << "\terrno=" << errno << "\tthis=" << this << "\tb=" << b << "\nBUF=" << (char*)buf << endl;

   return b;
}

int SocketTCP::write(void *buf, size_t n)
{
//   cout << __FILE__ << " (" << __LINE__ << ") " << "-> [" << pthread_self() << "] SocketTCP::Write; this=" << this << ", sd=" << sd << endl;   
   int b;

   do
   {
      b=send(sd, (char*)buf, n, 0);
//      cout << __FILE__ << " (" << __LINE__ << ") " << "errno = " << errno << endl;
   } while (errno==EINTR);
   if (b<0 || (errno!=0 && errno!=EAGAIN))
      throw SocketException(string("Error writing to socket"));

   tc.addW(b);
  // cout << __FILE__ << " (" << __LINE__ << ") " << "<- [" << pthread_self() << "] SocketTCP::Write; this=" << this << ", sd=" << sd << endl;   
//   cout << __FILE__ << " (" << __LINE__ << ") " << "[" << pthread_self() << "] [WRITE]\tSD=" << sd << "\terrno=" << errno << "\tthis=" << this << "\tb=" << b << "\nBUF=" << (char*)buf << endl;
   
   return b;
}

SocketTCP *SocketTCP::establish()
{
   switch (type)
   {
    case SOCK_CONNECT:
      if (connect(sd, (struct sockaddr*)&saddrr, sizeof(struct sockaddr_in))<0)
	  {
		/*  char buf2[80];
		  sprintf(buf2, "%d", WSAGetLastError());
		MessageBox(NULL, buf2, "error", MB_OK);*/

		throw SocketException(string("Unable to connect to ").
			       append(addr.toString()));
	  }

      return this;
      break;

    case SOCK_SERVE:
      {
	 int t=sizeof(struct sockaddr_in);
	 SOCKET s;
	 struct sockaddr_in saddrrnew;
	 memset(&saddrrnew, 0, sizeof(struct sockaddr_in));
	 
	 if ((s=accept(sd, (struct sockaddr*)&saddrrnew, &t))<0)
	 {
//	    cout << __FILE__ << " (" << __LINE__ << ") " << "ERROR " << errno << endl;
	    throw SocketException(string("Unable to accept connections on ").
			       append(addr.toString()));
	 }
	 
	 IP iptmp(saddrrnew.sin_addr.s_addr);
	 Address addrtmp(iptmp, htons(saddrrnew.sin_port));
	 
	 SocketTCP *stcp=new SocketTCP(*this);
	 stcp->sd=s;
	 stcp->addr=addrtmp;
	 
	 memmove(&stcp->saddrr, &saddrrnew, sizeof(struct sockaddr_in));
	 
	 return stcp;
      }
      break;
   }
//   cout << __FILE__ << " (" << __LINE__ << ") " << "[" << pthread_self() << "] [ESTABLISH]\tSD=" << sd << "\terrno=" << errno << "\tthis=" << this << endl;
   
}


void SocketTCP::close()
{
   if (sd!=-1)
   {
      closesocket(sd);
//      cout << __FILE__ << " (" << __LINE__ << ") " << "[" << pthread_self() << "] [CLOSE]\tSD=" << sd << "\terrno=" << errno << "\tthis=" << this << endl;   
      sd=-1;
   }
}
